Conversation
Summary of ChangesHello @timon-schelling, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly refactors the project's build and execution mechanisms by introducing a new Rust-based command-line tool. This tool consolidates various Highlights
Changelog
Ignored Files
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a new building crate to replace the root package.json scripts for running and building the project, which is a great simplification. However, I've found a few issues in the implementation of this new tool. The command runner can panic on empty command strings, and the argument parsing logic doesn't correctly handle all the commands mentioned in the updated documentation, specifically cargo run build and cargo run -- production. I've provided suggestions to fix these issues, along with some minor improvements for code clarity and correctness.
| impl From<&str> for Profile { | ||
| fn from(arg: &str) -> Self { | ||
| match arg { | ||
| "release" => Profile::Release, |
There was a problem hiding this comment.
The documentation was updated to use cargo run -- production, but this from implementation doesn't handle the "production" string. This will result in an error. You should also handle "production" as a Profile::Release.
| "release" => Profile::Release, | |
| "release" | "production" => Profile::Release, |
| let comand = comand.split_whitespace().collect::<Vec<_>>(); | ||
| let mut cmd = process::Command::new(comand[0]); | ||
| if comand.len() > 1 { | ||
| cmd.args(&comand[1..]); | ||
| } |
There was a problem hiding this comment.
The current implementation will panic if an empty command string is passed to run_from, because comand.split_whitespace().collect::<Vec<_>>() will be an empty vector, and comand[0] will be an out-of-bounds access. This can be handled more robustly and efficiently by using an iterator directly, which also avoids an intermediate allocation.
let mut parts = comand.split_whitespace();
let Some(program) = parts.next() else { return };
let mut cmd = process::Command::new(program);
cmd.args(parts);
tools/building/src/main.rs
Outdated
| ["build", rest @ ..] => build_desktop(rest.into()), | ||
| _ => run_desktop(rest.into()), | ||
| }, | ||
| rest => run_web(rest.into()), |
There was a problem hiding this comment.
The documentation was updated to use cargo run build, but this command is not handled here. It will fall into the rest case, which will try to parse "build" as a profile, fail, and show the usage message. You should add a case to handle the build command directly, which can default to building the web version.
["build", rest @ ..] => build_web(rest.into()),
rest => run_web(rest.into()),
tools/building/src/lib.rs
Outdated
|
|
||
| impl From<&[&str]> for Profile { | ||
| fn from(arg: &[&str]) -> Self { | ||
| arg.first().map(|s| s.to_string()).as_deref().unwrap_or_default().into() |
| run_from(comand, Some("frontend")); | ||
| } | ||
|
|
||
| pub fn run_from(comand: &str, dir: Option<&str>) { |
There was a problem hiding this comment.
There's a typo in the parameter name comand. It should be command. This typo appears in several places in this file (run, run_in_frontend_dir, run_from and its body). Please correct it throughout the file for consistency and clarity.
| pub fn run_from(comand: &str, dir: Option<&str>) { | |
| pub fn run_from(command: &str, dir: Option<&str>) { |
16fca60 to
8cb8c29
Compare
8cb8c29 to
b7e0fac
Compare
b7e0fac to
f0f3969
Compare
f0f3969 to
deb2c6d
Compare
There was a problem hiding this comment.
6 issues found across 33 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".nix/default.nix">
<violation number="1" location=".nix/default.nix:47">
P2: The `// inputs` merge gives flake inputs precedence over explicitly constructed args. If a flake input name ever collides with a key like `lib`, `pkgs`, or `system`, the carefully constructed value will be silently overridden. Reverse the merge order so explicit values take precedence:
```nix
args = inputs // {
inherit system;
...
};
```</violation>
</file>
<file name="tools/building/src/lib.rs">
<violation number="1" location="tools/building/src/lib.rs:74">
P1: The `ExitStatus` returned by `.wait()` is silently discarded. If the spawned command exits with a non-zero status (i.e., fails), execution continues to the next command without any error. For a build tool where commands are chained sequentially, this means a failed step won't stop the pipeline. Check the exit status and panic (or return an error) on failure.</violation>
</file>
<file name="tools/third-party-licenses/src/main.rs">
<violation number="1" location="tools/third-party-licenses/src/main.rs:63">
P3: Prefer returning `ExitCode` from `main()` instead of calling `std::process::exit(1)`. This avoids bypassing Rust's stack unwinding and is the idiomatic way to signal a non-zero exit code now that the heavy lifting is in `run()`.
(Based on your team's feedback about avoiding `exit()` and preferring returning for cleanup.) [FEEDBACK_USED]</violation>
</file>
<file name="desktop/bundle/src/common.rs">
<violation number="1" location="desktop/bundle/src/common.rs:50">
P2: Return an error instead of panicking so callers can handle command failures via the Result the function already exposes.</violation>
</file>
<file name=".github/workflows/comment-!build-commands.yml">
<violation number="1" location=".github/workflows/comment-!build-commands.yml:85">
P0: The `!build-debug` command will never work. The job-level `if` condition (line 24) still gates on `!build-dev`, so a `!build-debug` comment won't even start the job. The job-level condition, the usage comment (line 3), and the error message (line 92) all need to be updated from `!build-dev` to `!build-debug` to match this rename.</violation>
</file>
<file name="tools/building/src/deps.rs">
<violation number="1" location="tools/building/src/deps.rs:158">
P2: Bug: terminal check is on `stdout`, but the prompt is written to `stderr` and input is read from `stdin`. If `stdout` is redirected but `stdin`/`stderr` are terminals, this incorrectly skips the interactive prompt. The check should be on `std::io::stdin()` to determine if a user can respond interactively.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| run: | | ||
| if [[ "${{ github.event.comment.body }}" == "!build-dev" ]]; then | ||
| echo "command=build-dev" >> $GITHUB_OUTPUT | ||
| if [[ "${{ github.event.comment.body }}" == "!build-debug" ]]; then |
There was a problem hiding this comment.
P0: The !build-debug command will never work. The job-level if condition (line 24) still gates on !build-dev, so a !build-debug comment won't even start the job. The job-level condition, the usage comment (line 3), and the error message (line 92) all need to be updated from !build-dev to !build-debug to match this rename.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/comment-!build-commands.yml, line 85:
<comment>The `!build-debug` command will never work. The job-level `if` condition (line 24) still gates on `!build-dev`, so a `!build-debug` comment won't even start the job. The job-level condition, the usage comment (line 3), and the error message (line 92) all need to be updated from `!build-dev` to `!build-debug` to match this rename.</comment>
<file context>
@@ -82,10 +82,10 @@ jobs:
run: |
- if [[ "${{ github.event.comment.body }}" == "!build-dev" ]]; then
- echo "command=build-dev" >> $GITHUB_OUTPUT
+ if [[ "${{ github.event.comment.body }}" == "!build-debug" ]]; then
+ echo "command=build debug" >> $GITHUB_OUTPUT
elif [[ "${{ github.event.comment.body }}" == "!build-profiling" ]]; then
</file context>
| .unwrap_or_else(|e| { | ||
| panic!("Failed to run command '{}': {e}", comand.join(" ")); | ||
| }) | ||
| .wait() |
There was a problem hiding this comment.
P1: The ExitStatus returned by .wait() is silently discarded. If the spawned command exits with a non-zero status (i.e., fails), execution continues to the next command without any error. For a build tool where commands are chained sequentially, this means a failed step won't stop the pipeline. Check the exit status and panic (or return an error) on failure.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/building/src/lib.rs, line 74:
<comment>The `ExitStatus` returned by `.wait()` is silently discarded. If the spawned command exits with a non-zero status (i.e., fails), execution continues to the next command without any error. For a build tool where commands are chained sequentially, this means a failed step won't stop the pipeline. Check the exit status and panic (or return an error) on failure.</comment>
<file context>
@@ -0,0 +1,78 @@
+ .unwrap_or_else(|e| {
+ panic!("Failed to run command '{}': {e}", comand.join(" "));
+ })
+ .wait()
+ .unwrap_or_else(|e| {
+ panic!("Failed to wait for command '{}': {e}", comand.join(" "));
</file context>
| inherit info; | ||
| inherit deps; | ||
| } | ||
| // inputs; |
There was a problem hiding this comment.
P2: The // inputs merge gives flake inputs precedence over explicitly constructed args. If a flake input name ever collides with a key like lib, pkgs, or system, the carefully constructed value will be silently overridden. Reverse the merge order so explicit values take precedence:
args = inputs // {
inherit system;
...
};Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .nix/default.nix, line 47:
<comment>The `// inputs` merge gives flake inputs precedence over explicitly constructed args. If a flake input name ever collides with a key like `lib`, `pkgs`, or `system`, the carefully constructed value will be silently overridden. Reverse the merge order so explicit values take precedence:
```nix
args = inputs // {
inherit system;
...
};
```</comment>
<file context>
@@ -0,0 +1,81 @@
+ inherit info;
+ inherit deps;
+ }
+ // inputs;
+ in
+ args
</file context>
| let status = Command::new(program).args(args).stdout(Stdio::inherit()).stderr(Stdio::inherit()).status()?; | ||
| if !status.success() { | ||
| std::process::exit(1); | ||
| panic!("Command '{}' with args {:?} failed with status: {}", program, args, status); |
There was a problem hiding this comment.
P2: Return an error instead of panicking so callers can handle command failures via the Result the function already exposes.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At desktop/bundle/src/common.rs, line 50:
<comment>Return an error instead of panicking so callers can handle command failures via the Result the function already exposes.</comment>
<file context>
@@ -45,7 +47,7 @@ pub(crate) fn build_bin(package: &str, bin: Option<&str>) -> Result<PathBuf, Box
let status = Command::new(program).args(args).stdout(Stdio::inherit()).stderr(Stdio::inherit()).status()?;
if !status.success() {
- std::process::exit(1);
+ panic!("Command '{}' with args {:?} failed with status: {}", program, args, status);
}
Ok(())
</file context>
| eprintln!("See: https://graphite.art/volunteer/guide/project-setup/"); | ||
| } | ||
|
|
||
| if !installable.is_empty() && std::io::stdout().is_terminal() { |
There was a problem hiding this comment.
P2: Bug: terminal check is on stdout, but the prompt is written to stderr and input is read from stdin. If stdout is redirected but stdin/stderr are terminals, this incorrectly skips the interactive prompt. The check should be on std::io::stdin() to determine if a user can respond interactively.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/building/src/deps.rs, line 158:
<comment>Bug: terminal check is on `stdout`, but the prompt is written to `stderr` and input is read from `stdin`. If `stdout` is redirected but `stdin`/`stderr` are terminals, this incorrectly skips the interactive prompt. The check should be on `std::io::stdin()` to determine if a user can respond interactively.</comment>
<file context>
@@ -0,0 +1,185 @@
+ eprintln!("See: https://graphite.art/volunteer/guide/project-setup/");
+ }
+
+ if !installable.is_empty() && std::io::stdout().is_terminal() {
+ eprintln!();
+ eprintln!("The following can be installed automatically:");
</file context>
| fn main() { | ||
| if let Err(e) = run() { | ||
| eprintln!("Error: {e}"); | ||
| std::process::exit(1); |
There was a problem hiding this comment.
P3: Prefer returning ExitCode from main() instead of calling std::process::exit(1). This avoids bypassing Rust's stack unwinding and is the idiomatic way to signal a non-zero exit code now that the heavy lifting is in run().
(Based on your team's feedback about avoiding exit() and preferring returning for cleanup.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/third-party-licenses/src/main.rs, line 63:
<comment>Prefer returning `ExitCode` from `main()` instead of calling `std::process::exit(1)`. This avoids bypassing Rust's stack unwinding and is the idiomatic way to signal a non-zero exit code now that the heavy lifting is in `run()`.
(Based on your team's feedback about avoiding `exit()` and preferring returning for cleanup.) </comment>
<file context>
@@ -39,6 +58,13 @@ struct Run<'a> {
fn main() {
+ if let Err(e) = run() {
+ eprintln!("Error: {e}");
+ std::process::exit(1);
+ }
+}
</file context>
depends on #3808